package com.core.indexer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.core.indexer.IndexData.DocList;
import com.core.indexer.IndexData.Lexicon;

public class SearchCore {
	
	boolean mDebug;
	Map<String, Double> mMapDocWeight;
	
	public void DoSearch(String type, String queryLabel, int numResults, String strLexicon, String strInvLists, String strMap, String strStopList, ArrayList<String> arrQueryTerms)
	{
		DoSearch(type, queryLabel, numResults, strLexicon, strInvLists, strMap, strStopList, arrQueryTerms, 0);
	}
	
	public void DoSearch(String strLexicon, String strInvLists, String strMap, ArrayList<String> arrQueryTerms)
	{
		DoSearch("-BM25", "001", 10, strLexicon, strInvLists, strMap, "stoplist", arrQueryTerms, 0);
	}
	
	
	public void DoSearch(String type, String queryLabel, int numResults, String strLexicon, String strInvLists, String strMap, String strStopList, ArrayList<String> arrQueryTerms, int aditionalQueryNum)
	{
		IndexData.Get().Init(true);
		ArrayList<DocList> mDocPosToTitle;
		mMapDocWeight = new HashMap<String, Double>();
		try {
			FileManager.Get().InitForSearch(strMap, strLexicon, strInvLists);
			mDocPosToTitle = new ArrayList<IndexData.DocList>();
			Set<String> stopList = FileManager.Get().loadStoplist(strStopList);
			FileManager.Get().loadMap(mDocPosToTitle, mMapDocWeight);
			FileManager.Get().loadLexicon();
			Accumulator.Get().Init(this);
			if(type.compareToIgnoreCase("-BM25") == 0
					|| type.compareToIgnoreCase("-PRF") == 0)
				BM25.Get().Init(mDocPosToTitle);
			
			long startTime = System.currentTimeMillis();
			for(String query : arrQueryTerms)
			{
				if(stopList != null && stopList.contains(query))
					continue;
				
				Lexicon lexicon = IndexData.Get().GetLexiconByKeyword(query);
				if(lexicon != null)
				{
					Map<Integer, Integer> invlist = new HashMap<Integer, Integer>();
					FileManager.Get().loadInvlist(lexicon.GetCount(), lexicon.getInvertedListOffset(), invlist);
					for(Integer key : invlist.keySet())
					{
						if(key >= mDocPosToTitle.size())
						{
							System.out.println(String.format("ERROR: [%d] out of document range", key));
							continue;
						}
						double docScore = 0.0;
						if(type.compareToIgnoreCase("-BM25") == 0)
						{
							docScore = BM25.Get().GetScore(mDocPosToTitle.get(key).DocSize, invlist.size(), invlist.get(key));
						}
						Accumulator.Get().InsertData(mDocPosToTitle.get(key).DocNo, docScore);
					}
				}
			}
			
			if(type.compareToIgnoreCase("-PRF") == 0 && aditionalQueryNum > 0)
			{
				double dInitialR = Accumulator.Get().GetR();
				Map<String, Lexicon> mapLexicon = IndexData.Get().getAllLexicon();
				for(String key : mapLexicon.keySet())
				{
					if(stopList != null && stopList.contains(key))
						continue;
					if(arrQueryTerms.contains(key))
						continue;
					
					Lexicon lexicon = mapLexicon.get(key);
					int ft = lexicon.GetCount();
					Map<Integer, Integer> invlist = new HashMap<Integer, Integer>();
					FileManager.Get().loadInvlist(lexicon.GetCount(), lexicon.getInvertedListOffset(), invlist);
					
					int rt = 0;
					for(Integer docPos : invlist.keySet())
					{
						if(Accumulator.Get().HasDocument(mDocPosToTitle.get(docPos).DocNo))
							rt++;
					}
					
					if(rt > 0)
					{
						PRF.Get().CalculateTSV(lexicon.GetKeyWord(), dInitialR, rt, ft, mDocPosToTitle.size());
					}
				}
				
				
				ArrayList<String> aditionalQuery = PRF.Get().GetNewQuery(aditionalQueryNum);
				
				System.out.println("Added terms: ");
				for(String query : aditionalQuery)
				{
					if(stopList != null && stopList.contains(query))
						continue;
					
					System.out.print(query + " ");
					Lexicon lexicon = IndexData.Get().GetLexiconByKeyword(query);
					if(lexicon != null)
					{
						Map<Integer, Integer> invlist = new HashMap<Integer, Integer>();
						FileManager.Get().loadInvlist(lexicon.GetCount(), lexicon.getInvertedListOffset(), invlist);
						for(Integer key : invlist.keySet())
						{
							if(key >= mDocPosToTitle.size())
							{
								System.out.println(String.format("ERROR: [%d] out of document range", key));
								continue;
							}
							double docScore = 0.0;
							double wt = PRF.Get().GetWT(lexicon.GetKeyWord(), dInitialR, mDocPosToTitle.size());
							
							wt = wt/3;
							docScore = BM25.Get().GetScore(mDocPosToTitle.get(key).DocSize, invlist.size(), invlist.get(key), wt);
							Accumulator.Get().InsertData(mDocPosToTitle.get(key).DocNo, docScore);
						}
					}
				}
				System.out.println();
			}
			
			Map<String, Double> docList = Accumulator.Get().PostProcess();
			if(docList.size() > 1)
			{
				ArrayList<IndexData.DocList> result = MinHeap.Get().HeapifyList(docList, numResults);
				Print(result, queryLabel);
			}
			else if(docList.size() == 1)
			{
				for(String key : docList.keySet())
				{
					System.out.println(String.format("%s %s %d %f", queryLabel, key, 0, docList.get(key)));
				}
				
			}
			else //No result found
			{
				System.out.println("No result!");
			}
			System.out.println(String.format("%d ms", System.currentTimeMillis() - startTime));
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
	
	public double GetDocWeight(String docNo)
	{
		return mMapDocWeight.get(docNo);
	}
	
	private void Print(ArrayList<IndexData.DocList> docList, String label)
	{
		int nPos = 1;
		for(int i = 0; i < docList.size(); i++)
		{
			System.out.println(String.format("%s %s %d %f", label, docList.get(i).DocNo, nPos, docList.get(i).DocScore));
			nPos++;
		}
	}
}
